W10. Hash Table Design
1. Theory
1.1 Dictionaries and Sets
1.1.1 The Dictionary Abstract Data Type
A dictionary (also called a map) is one of the most important and frequently used abstract data types in computer science. It stores a collection of key–value pairs, where each key is associated with exactly one value. You can think of a dictionary just like a real dictionary: you look up a word (the key) and get its definition (the value).
The three fundamental operations of a dictionary are:
put(k, v)— insert a key–value pair into the dictionary. If the key already exists, its old value is replaced by .get(k)— retrieve the value associated with key . ReturnsNIL(null) if is not found.remove(k)— remove the entry with key from the dictionary.
Critical property: for each key the dictionary stores at most one value. This is what distinguishes a dictionary from, say, a multimap.
1.1.2 Dictionary via Doubly Linked List
The simplest implementation stores all key–value pairs as nodes in a doubly linked list. For example, the dictionary
However, all operations on this implementation require scanning the entire list in the worst case:
get(k): — must traverse the list to find the key.put(k, v): — must check for duplicates before inserting.remove(k): — must find the node first; if given a direct reference.
This is too slow for large dictionaries. We need something better.
1.1.3 Dictionary via Array (Direct-Address Table)
If the keys happen to be integers in the range array[k] = v. This is called a direct-address table.
With a direct-address table, all three operations become
get(k): — index directly into the array.put(k, v): — assignarray[k] = v.remove(k): — setarray[k] = NIL.
The limitation is obvious: if the universe of keys
1.2 Hash Tables: Core Ideas
1.2.1 What Is a Hash Table?
A hash table combines the speed of an array with the flexibility of general keys. The key idea is:
- Allocate an array
of size (much smaller than ). - Use a hash function
to map each key to an index. - Store the key–value pair at index
in the array.
The main appeal: when things go well, all operations run in
1.2.2 Hash Functions
A hash function is a mapping:
where
Examples:
where
1.2.3 Collisions
A collision occurs when
(since and )
When the universe
- Chaining — store a list of all key–value pairs that hash to the same slot.
- Open addressing — find an alternative empty slot in the array itself.
1.3 Chaining
1.3.1 How Chaining Works
In chaining, each slot of the hash table array stores a linked list (typically doubly linked) of all entries whose keys hash to that slot. When we insert a new key
- Compute
. - Prepend the new entry to the linked list at slot
.
For example, if
1.3.2 Complexity of Chaining
Worst-case complexity: All
put(k, v): (if keys are distinct — prepend to the front); in the absolute worst case (if we must check for duplicates).get(k): — may need to scan the entire chain.remove(entry): — with doubly linked lists, given a pointer to the entry, removal is a constant-time pointer operation.
Average-case complexity is much better. It is parameterized by the load factor:
where
Average-case complexities:
put(k, v):get(k):remove(entry):
If
Note that for chaining,
1.4 Open Addressing
1.4.1 The Concept
In open addressing, the hash table array holds at most one key–value pair per slot. Instead of a separate list for collisions, when a collision occurs we probe the array for an alternative empty slot.
For each key
Each probe sequence is a permutation of
Important: since each slot holds at most one element, the load factor is bounded:
1.4.2 Linear Probing
In linear probing, after an initial hash
The probe sequence starting at
Advantage: simple and cache-friendly (accesses consecutive memory locations).
Disadvantage: primary clustering — long runs of occupied slots form clusters, making future insertions and lookups slower as the table fills up.
1.4.3 Double Hashing
In double hashing, after a collision at
The probe sequence visits slots
For the probe sequence to cover all
Advantage: eliminates primary clustering; the step size varies per key, spreading entries more uniformly.
1.4.4 Insertion, Search, and Deletion
Insertion follows the probe sequence until an empty slot is found:
HASH-INSERT(T, k)
1 i = 0
2 repeat
3 q = h(k, i)
4 if T[q] == NIL
5 T[q] = k
6 return q
7 else i = i + 1
8 until i == m
9 error "hash table overflow"
Search follows the same probe sequence, stopping at either the key or an empty slot:
HASH-SEARCH(T, k)
1 i = 0
2 repeat
3 q = h(k, i)
4 if T[q] == k
5 return q
6 i = i + 1
7 until T[q] == NIL or i == m
8 return NIL
Why searching stops at NIL: When we inserted key
Deletion is tricky. If we simply clear the slot, we may break the probe sequence for other keys: a later key
- Replace the deleted entry with a special DELETED marker.
- During search, treat DELETED slots as occupied (keep probing), but during insertion treat them as empty (can insert here).
The downside: DELETED markers accumulate and do not reduce the probe count. Over time, this degrades performance. For linear probing specifically, a more sophisticated deletion without markers is possible (see Cormen et al. 2022, §11.5.1), but this does not generalize to arbitrary probing strategies.
1.4.5 Complexity of Open Addressing
The load factor for open addressing is always
put(k, v): on averageget(k): on averageremove(k): hard to characterize (depends on the deletion strategy)
When
1.4.6 Comparison: Linked List vs. Chaining vs. Open Addressing
| Operation | Doubly linked list | Chaining | Open addressing |
|---|---|---|---|
put(k, v) |
|||
get(k) |
|||
remove(entry) |
depends on strategy | ||
| Load factor |
— |
1.5 Hash Functions in Depth
1.5.1 Two-Stage Hashing
In practice, hashing is often implemented in two stages:
- Hash code:
— maps the key to an integer (possibly very large or negative). - Compression:
— maps the integer to a valid table index.
This separation is natural because the hash code can be defined per data type (e.g., in a class’s hashCode() method in Java), while the compression function is part of the hash table implementation and depends on the table size
1.5.2 Hash Code Strategies
1. Object address in memory
Use the object’s memory address as the hash code.
- Pro: Fast — no computation needed beyond reading the pointer.
- Con: Two logically equal objects at different memory addresses get different hash codes. For example, two distinct
Stringobjects both containing"Ivan"would hash differently, breaking the expectation that equal keys map to the same slot.
2. Cast to integer
Reinterpret the bits of the key as an integer.
- Pro: Equal values always give equal hash codes.
- Con: For large keys (e.g., long strings), the resulting integer is impractically large and does not fit in a standard integer type.
3. Sum of components
Break the key into parts (e.g., bytes or words) and sum them.
- Pro: Equal values always give equal hash codes. Practical to compute.
- Con: Permutations of the same components produce identical hash codes. For example,
"abc"and"bca"and"cab"all have the same sum of character codes. This causes many collisions for sets of keys that are permutations of each other.
4. Polynomial in components (polynomial rolling hash)
Treat the key components as coefficients of a polynomial evaluated at some constant
- Pro: Equal values always give equal codes. Different orderings almost never collide (different polynomials evaluated at
). Can be computed efficiently using Horner’s method: . - Con: Slightly more expensive to compute than a simple sum, but still
for a key of length .
The polynomial hash is used in practice (e.g., Java’s String.hashCode()) and also appears in the Rabin–Karp string matching algorithm.
1.5.3 Compression Functions
1. Division method
- Simple and fast.
- Works well when
is a prime not close to a power of 2 or 10. Avoid (only uses the least significant bits) or (only uses the last decimal digits), as these cause systematic clustering.
2. Multiplication method
where
- Works for any
. - Slightly more expensive to compute.
- The value of
is less critical — often chosen as a power of 2 for efficient bit-shifting implementation.
1.5.4 Properties of an Ideal Hash Function
A good hash function for a hash table of size
- Uniformity: Keys from
hash uniformly into , meaning each slot is equally likely to be chosen. - Determinism: Equal keys always produce the same hash value.
- Speed: Runs in
or close to it.
Note: Hash functions for hash tables do not need to be one-way (hard to invert). Cryptographic hash functions (like SHA-256) are designed to be one-way, but this comes at a significant speed cost. Using a cryptographic hash in a hash table is wasteful and unnecessary.
1.6 Mathematical Analysis of Complexity
1.6.1 Chaining: Assumptions and Setup
For the mathematical analysis we assume:
- Hash table
has slots and stores key–value pairs. - Load factor
. - Simple uniform hashing: any key is equally likely to hash into any of the
slots, independently of all other keys. That is, for every key and every slot . - Expected time to evaluate the hash function is
.
Let
Under simple uniform hashing, the expected chain length at any slot is:
1.6.2 Chaining: Unsuccessful Search
Theorem (Cormen et al. 2022, Theorem 11.1). In a hash table resolving collisions by chaining, an unsuccessful search takes expected time
Proof. An unsuccessful search for key
Taking expectations:
1.6.3 Chaining: Successful Search
Theorem (Cormen et al. 2022, Theorem 11.2). In a hash table resolving collisions by chaining, a successful search takes expected time
Proof. Assume the
When searching for key
Define indicator variables:
The expected number of elements examined during the search (beyond
Including the cost of computing the hash and examining
1.6.4 Open Addressing: Assumptions
For open addressing analysis we assume:
- Hash table
has slots and stores key–value pairs. - Load factor
. - Uniform hashing: for each key
, the probe sequence is equally likely to be any one of the permutations of , independently of other keys. - Expected time to evaluate the hash function is
.
1.6.5 Open Addressing: Unsuccessful Search
Theorem (Cormen et al. 2022, Theorem 11.6). The expected number of probes in an unsuccessful search is at most
Proof. Let
The inequality uses the fact that
Using the formula
Intuition: The formula
1.6.6 Open Addressing: Successful Search
Theorem (Cormen et al. 2022, Theorem 11.8). The expected number of probes in a successful search is at most
Proof. Searching for key
Averaging uniformly over all
We bound the sum by an integral:
Therefore:
2. Definitions
- Dictionary (Map): An abstract data type that stores a collection of key–value pairs, supporting
put(k,v),get(k), andremove(k). Each key maps to at most one value. - Hash table: A data structure implementing a dictionary using an array
of size together with a hash function to map keys to array indices. - Hash function: A function
mapping keys from universe to array indices. The value is the hash of key . - Collision: The situation where two distinct keys
hash to the same index: . - Load factor (
): The ratio of the number of stored entries to the table size . Measures how full the table is. - Chaining: A collision resolution strategy where each table slot holds a linked list of all entries mapping to that slot.
- Open addressing: A collision resolution strategy where each slot holds at most one entry; collisions are resolved by probing for another empty slot using a probe sequence.
- Probe sequence: The sequence of slots
that open addressing examines for key . Each probe sequence is a permutation of . - Linear probing: An open addressing strategy with
. - Double hashing: An open addressing strategy with
. - Primary clustering: A phenomenon in linear probing where occupied slots form long consecutive runs, degrading performance.
- DELETED marker: A special sentinel value placed in a slot when its entry is deleted from an open-address hash table, allowing search to continue probing past the deleted slot.
- Hash code: The first stage of two-stage hashing:
maps a key to an arbitrary integer. - Compression function: The second stage of two-stage hashing:
maps the integer hash code to a valid table index. - Simple uniform hashing: An assumption that each key is equally likely to hash to any slot, independently of all other keys.
- Uniform hashing: A stronger assumption used for open addressing analysis: each key’s probe sequence is equally likely to be any of the
permutations of slots, independently. - Direct-address table: A special case of a hash table where keys are integers in
and (identity function). Provides worst-case for all operations when feasible.
3. Formulas
- Load factor:
, where = number of stored keys, = table size. - Expected chain length (chaining):
for each slot , under simple uniform hashing. - Chaining — unsuccessful search:
expected time. - Chaining — successful search:
expected time. - Open addressing probe formula (linear probing):
- Open addressing probe formula (double hashing):
- Open addressing — unsuccessful search: expected probes
- Open addressing — successful search: expected probes
- Division method (compression):
- Multiplication method (compression):
, where - Polynomial hash code:
- Geometric series identity (used in proofs):
for
4. Practice
4.1. Linear Probing with Quadratic Hash Function (Problem Set 8, Task 1)
Consider a hash table with 11 slots (indices
Insert the following keys in the given order using linear probing to resolve collisions:
(a) Show the final state of the hash table.
(b) For key 31, list the sequence of indices probed until 31 is placed (including the final slot).
(c) After all insertions, what is the maximum number of probes an unsuccessful search can make?
Click to see the solution
Key Concept: First compute
Compute
for each key:4 7 19 27 8 15 11 23 6 31 14 2 Insert each key with linear probing:
: , slot 7 empty → place at 7. : , slot 9 empty → place at 9. : , slot 10 empty → place at 10. : , slot 9 occupied → try 10 (occupied) → try ; empty → place at 0. : , slot 7 occupied → try 8; empty → place at 8. : , slot 4 empty → place at 4. : , slot 9 occupied → try 10 (occupied) → try 0 (occupied) → try 1; empty → place at 1. : , slot 4 occupied → try 5; empty → place at 5. : , slot 9 occupied → try 10 (occupied) → try 0 (occupied) → try 1 (occupied) → try 2; empty → place at 2. : , slot 6 empty → place at 6. : , slot 7 occupied → try 8 (occupied) → try 9 (occupied) → try 10 (occupied) → try 0 (occupied) → try 1 (occupied) → try 2 (occupied) → try 3; empty → place at 3.
Final table state:
Index 0 1 2 3 4 5 6 7 8 9 10 Key 8 23 31 2 11 6 14 4 15 19 27
(b) Probe sequence for
Probe sequence:
(c) Maximum probes for unsuccessful search:
The table is completely full (all 11 slots occupied). An unsuccessful search for a key
Answer:
(a) Final table as shown in step 3.
(b) Probe sequence for 31: indices
(c) Maximum unsuccessful probes = 11 (the entire table is full, so the search exhausts all slots before finding no match).
4.2. Dictionary of Dictionaries: Complexity Analysis (Problem Set 8, Task 2)
Consider a dictionary
Assume the outer dictionary has
(a) Fill in the following tables with asymptotic expected times for unsuccessful and successful search in
(b) With
Click to see the solution
Key Concept: Searching for pair
Recall the expected search times for each implementation:
| Method | Unsuccessful | Successful |
|---|---|---|
| Chaining | ||
| Open addressing | ||
| AVL tree |
For the outer dictionary
(a) Unsuccessful search (key
An unsuccessful search fails at either step: we search for
| Chaining | Open addr. | AVL tree | |
|---|---|---|---|
| Chaining | |||
| Open addr. | |||
| AVL tree |
Successful search (key
| Chaining | Open addr. | AVL tree | |
|---|---|---|---|
| Chaining | |||
| Open addr. | |||
| AVL tree |
(b) With
Compute the successful search cost for each cell:
- Chaining with
: (constant). - Open addressing with
: , so (also constant, slightly more than chaining). - AVL tree:
or — grows with table size.
The minimum is achieved by any combination using chaining for both
Open addressing also gives
Answer: Any
4.3. Hash Function Analysis (Problem Set 8, Task 3)
Let
(a) For each slot
(b) Exhibit two distinct keys in
(c) With chaining and table size 7, what is the largest possible chain after inserting all 10 keys?
(d) Find an integer
Click to see the solution
Key Concept: Compute
(a) Slot assignments for
| 2 | 4 | 4 |
| 3 | 6 | 6 |
| 5 | 10 | 3 |
| 7 | 14 | 0 |
| 10 | 20 | 6 |
| 12 | 24 | 3 |
| 17 | 34 | 6 |
| 19 | 38 | 3 |
| 23 | 46 | 4 |
| 31 | 62 | 6 |
Slots used:
(b) Colliding pairs:
Many pairs collide. For example:
— keys and collide at slot 3. — keys and collide at slot 6.
(c) Largest possible chain:
Slot 6 receives keys
(d) Injectivity of
We need all 10 values
Check
| 2 | 3 | 5 | 7 | 10 | 12 | 17 | 19 | 23 | 31 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 3 | 5 | 7 | 0 | 2 | 7 | 9 | 3 | 1 |
We already see collisions:
Since we need
Answer:
(a) Slots never hit by any key:
(b) Example collision:
(c) Largest possible chain: 4 (slot 6 receives keys 3, 10, 17, 31).
(d) No such
4.4. Hash Table with Chaining (Lecture 8, Task 1)
Insert keys
Click to see the solution
Key Concept: For each key, compute
Compute hash values:
Key Slot 5 5 5 28 1 1 19 1 1 15 6 6 20 2 2 33 6 6 12 3 3 17 8 8 10 1 1 Build the chains (new elements are prepended, so the last inserted appears at the head):
- Slot 0: empty
- Slot 1:
- Slot 2:
- Slot 3:
- Slot 4: empty
- Slot 5:
- Slot 6:
- Slot 7: empty
- Slot 8:
Answer: The hash table has chains at slots 1, 2, 3, 5, 6, 8. Slot 1 has the longest chain:
4.5. Hash Table with Double Hashing (Lecture 8, Task 2)
Insert keys
Click to see the solution
Key Concept: Compute the initial slot
Compute
, , and initial slot :50 7 6 7 3 4 17 7 8 9 5 6 12 2 34 2 Insert each key:
: slot 7 is empty → place at 7. : slot 7 occupied ( ) → probe ; slot 9 empty → place at 9. : slot 4 is empty → place at 4. : slot 7 occupied ( ) → probe ; slot 10 empty → place at 10. : slot 9 occupied ( ) → probe ; slot 2 empty → place at 2. : slot 6 is empty → place at 6. : slot 2 occupied ( ) → probe ; slot 5 empty → place at 5. : slot 2 occupied ( ) → probe ; slot 7 occupied → probe ; slot 1 empty → place at 1.
Final table state:
Index 0 1 2 3 4 5 6 7 8 9 10 Key — 34 8 — 3 12 5 50 — 6 17
Answer: Final table as shown above. No slot required more than 3 probes.
4.6. Hash Table with Linear Probing (Lecture 8, Task 3)
Insert keys
Click to see the solution
Key Concept: Compute
Compute initial slots:
50 7 6 7 3 4 17 7 8 9 5 6 12 2 34 2 Insert each key (linear probing +1 on collision):
: slot 7 empty → place at 7. : slot 7 occupied → try 8; empty → place at 8. : slot 4 empty → place at 4. : slot 7 occupied → try 8 (occupied) → try 9; empty → place at 9. : slot 9 occupied → try 10; empty → place at 10. : slot 6 empty → place at 6. : slot 2 empty → place at 2. : slot 2 occupied → try 3; empty → place at 3.
Final table state:
Index 0 1 2 3 4 5 6 7 8 9 10 Key — — 12 34 3 — 5 50 6 17 8
Answer: Final table as shown. Notice that
4.7. Effect of Sorted Chains on Complexity (Lecture 8, Task 4)
How do worst-case and average-case key-search complexity change for a chained hash table if each chain is a sorted array and we perform a binary search within the array?
Click to see the solution
Key Concept: The standard chaining implementation uses linked lists, giving
(a) Worst case:
In the worst case all
- Standard chaining (linked list): worst-case search is
(linear scan). - Sorted array + binary search: worst-case search is
— we can binary search the sorted array.
So the worst-case search improves from
(b) Average case (under simple uniform hashing):
Under simple uniform hashing, the expected chain length is
- Standard chaining:
on average. - Sorted array + binary search:
on average.
If
(c) Cost of insertion:
There is a trade-off: maintaining a sorted array requires finding the correct insertion position (put(k, v) becomes
Answer: Worst-case search improves from
4.8. Open Addressing: Bounds on Number of Probes (Lecture 8, Task 5)
Compute upper bounds on the expected number of probes for unsuccessful and successful search in an open-address hash table when:
(a)
(b)
Click to see the solution
Key Concept: Use the two theorems:
- Unsuccessful search:
- Successful search:
(a)
- Unsuccessful:
probes. - Successful:
probes.
(b)
- Unsuccessful:
probes. - Successful:
probes.
Answer:
| Unsuccessful (upper bound) | Successful (upper bound) | |
|---|---|---|
| 4 | ||
| 8 |
Note that doubling the “fullness” (going from